using System;
using System.Collections.Generic;
using System.Configuration;
using System.Text;
using StructureMap;

using Impact.Infrastructure.Exceptions;
using Impact.Infrastructure.Configuration;
using Impact.Infrastructure.State;
using Impact.Views;
using Impact.Model.Objects;
using Impact.Model.Service;
using Impact.Infrastructure;

namespace Impact.Controllers
{
    /// <summary>
    /// this controller is responsible for controlling the flow and logic for maintaining articles
    /// </summary>
    public class ArticleController
    {
        private IArticleEditorView view;
        private IArticleService service;

        /// <summary>
        /// default constructor
        /// </summary>
        public ArticleController(IArticleEditorView view)
        {
            Check.Require(view, "A valid IArticleEditorView is required");
            this.view = view;
            this.service = ObjectFactory.GetInstance<IArticleService>();
        }

        /// <summary>
        /// initialize the members of this controller
        /// </summary>
        /// <param name="isFirstLoad"></param>
        public void Initialize(bool isFirstLoad)
        {
            Check.Require(view, "view is not initialized");
            if (isFirstLoad)
            {
                view.PopulateForm();
            }
        }

        /// <summary>
        /// saves the given article
        /// </summary>
        public void SaveArticle()
        {
            Check.Require(view, "view is not initialized");
            CommitArticleData(view.GetArticleData(), view.GetTags());
        }



        private static void CommitArticleData(Article article, List<string> newTags)
        {
            Check.Require(Transaction.Current.UserId.HasValue, "User must be logged in");
            Guid userId = Transaction.Current.UserId.Value;

            ITagService tagService = ObjectFactory.GetInstance<ITagService>();
            IArticleService articleService = ObjectFactory.GetInstance<IArticleService>();

            // first commit the article.  for new articles, gets the articleId to use for tags
            articleService.Save(article, userId);

            // create records for new tags typed in by the user
            // note: this TagCollection constructor will first attempt to load the tag from
            // the db.  if it can't, then it creates a new one.  this is to avoid duplicates
            TagCollection newTagCollection = new TagCollection(newTags);
            newTagCollection.SaveAll();

            // add existing tags to the list of tags to be applied to the article
            foreach(ArticleTag existingTag in article.Tags)
            {
                if (!newTags.Contains(existingTag.Text))
                {
                    newTags.Add(existingTag.Text);
                }
            }

            // remove all tags currently attached to the article
            articleService.RemoveAllTags(article.ArticleId);

            // now attach all new and existing tags to the article
            foreach (string tagText in newTags)
            {
                Tag attachTag = tagService.FetchByText(tagText);
                articleService.AddTag(article.ArticleId, attachTag.TagId, userId);
            }

        }

        /// <summary>
        /// Retrieve an article based on the ID
        /// </summary>
        public Article GetArticle(int articleId)
        {
            return service.FetchByID(articleId);
        }

        

        #region Static Methods

        /// <summary>
        /// get all categories
        /// </summary>
        public static ArticleCategoryCollection GetAllCategories(bool onlyVisible)
        {
            ICategoryService service = ObjectFactory.GetInstance<ICategoryService>();
            return service.FetchAll(onlyVisible);
        }

        /// <summary>
        /// get all the articles for the given category
        /// </summary>
        public static ArticleCollection GetCategoryArticles(int categoryId, bool onlyEnabled)
        {
            Check.Require(categoryId > 0, "a valid category ID is required");
            IArticleService service = ObjectFactory.GetInstance<IArticleService>();
            return service.FetchByCategory(categoryId, onlyEnabled);
        }

        /// <summary>
        /// Retrieve all articles in the given category by name
        /// </summary>
        public static ArticleCollection GetCategoryArticles(string nameKey, bool onlyEnabled)
        {
            Check.Require(!string.IsNullOrEmpty(nameKey), "Name Key is required");
            IArticleService service = ObjectFactory.GetInstance<IArticleService>();
            return service.FetchByCategory(nameKey, onlyEnabled);
        }

        /// <summary>
        /// adds a new category with the given name
        /// </summary>
        public static void AddCategory(string name)
        {
            Check.Require(!string.IsNullOrEmpty(name), "a valid non-empty string is required for the name of the category");

            ArticleCategory category = new ArticleCategory();
            category.Name = name;
            category.NameKey = Utility.StripNonAlphaNumeric(name);

            ICategoryService service = ObjectFactory.GetInstance<ICategoryService>();
            service.Save(category, Transaction.Current.UserId.Value);
        }

        /// <summary>
        /// deletes the given article
        /// </summary>
        public static void DeleteArticle(int articleId)
        {
            Check.Require(articleId > 0, "a valid ArticleId is required");
            IArticleService service = ObjectFactory.GetInstance<IArticleService>();
            service.Remove(articleId);
        }

        /// <summary>
        /// deletes the given category
        /// </summary>
        public static void DeleteArticleCategory(int categoryId)
        {
            Check.Require(categoryId > 0, "a valid CategoryId is required");
            ICategoryService service = ObjectFactory.GetInstance<ICategoryService>();
            service.Remove(categoryId);
        }

        /// <summary>
        /// toggles whether the given article is enabled on the front end
        /// </summary>
        public static void ToggleEnabled(int articleId)
        {
            Check.Require(articleId > 0, "a valid ArticleId is required");
            IArticleService service = ObjectFactory.GetInstance<IArticleService>();
            Article article = service.FetchByID(articleId);
            article.Enabled = !article.Enabled;
            service.Save(article, Transaction.Current.UserId.Value);
        }

        /// <summary>
        /// for the given category, toggles whether comments are allowed
        /// </summary>
        public static void ToggleCategoryCommentsAllowed(int categoryId)
        {
            Check.Require(categoryId, "CategoryId is required");
            ICategoryService service = ObjectFactory.GetInstance<ICategoryService>();
            ArticleCategory category = service.FetchByID(categoryId);
            category.AllowComments = !category.AllowComments;
            service.Save(category, Transaction.Current.UserId.Value);
        }

        /// <summary>
        /// for the given category, toggles whether RSS is enabled
        /// </summary>
        /// <param name="categoryId"></param>
        public static void ToggleRssAllowed(int categoryId)
        {
            Check.Require(categoryId, "CategoryId is required");
            ICategoryService service = ObjectFactory.GetInstance<ICategoryService>();
            ArticleCategory category = service.FetchByID(categoryId);
            category.AllowRss = !category.AllowRss;
            service.Save(category, Transaction.Current.UserId.Value);
        }

        /// <summary>
        /// for the given article, toggles whether comments are allowed
        /// </summary>
        public static void ToggleArticleCommentsAllowed(int articleId)
        {
            Check.Require(articleId, "ArticleId is required");
            IArticleService service = ObjectFactory.GetInstance<IArticleService>();
            Article article = service.FetchByID(articleId);
            article.AllowComments = !article.AllowComments;
            service.Save(article, Transaction.Current.UserId.Value);
        }

        /// <summary>
        /// for the given article, toggles the public visibility
        /// </summary>
        /// <param name="categoryId"></param>
        public static void ToggleCategoryVisible(int categoryId)
        {
            Check.Require(categoryId, "CategoryId is required");
            ICategoryService service = ObjectFactory.GetInstance<ICategoryService>();
            ArticleCategory category = service.FetchByID(categoryId);
            category.MenuVisible = !category.MenuVisible;
            service.Save(category, Transaction.Current.UserId.Value);
        }

        /// <summary>
        /// deletes the given comment
        /// </summary>
        public static void DeleteArticleComment(int commentId)
        {
            Check.Require(commentId > 0, "Must provide a valid comment ID");
            IArticleService service = ObjectFactory.GetInstance<IArticleService>();
            service.RemoveComment(commentId);
        }

        /// <summary>
        /// Retrieve all articles that are in categories that have RSS enabled
        /// </summary>
        public static ArticleCollection GetAllRssArticles()
        {
            ICategoryService categoryService = ObjectFactory.GetInstance<ICategoryService>();
            IArticleService articleService = ObjectFactory.GetInstance<IArticleService>();

            ArticleCategoryCollection categories = categoryService.FetchAll(true);
            ArticleCollection articles = new ArticleCollection();

            categories.GetList().ForEach(delegate(ArticleCategory category)
            {
                if (category.AllowRss)
                {
                    articles.AddRange(articleService.FetchByCategory(category.CategoryId, true));
                }
            });

            articles.GetList().Sort(delegate(Article a, Article b)
            {
                return b.CreatedOn.CompareTo(a.CreatedOn);
            });

            return articles;
        }

        /// <summary>
        /// toggles whether a tag is attached to an article or not
        /// </summary>
        /// <returns>true if the tag has been attached, false if it has been removed</returns>
        public static bool ToggleTag(int articleId, int tagId)
        {
            bool on;
            IArticleService service = ObjectFactory.GetInstance<IArticleService>();
            ArticleTagCollection tags = service.FetchTags(articleId);

            ArticleTag aTag = tags.GetList().Find(delegate(ArticleTag at)
            {
                return at.TagId.Equals(tagId);
            });

            if (aTag != null)
            {
                service.RemoveTag(aTag.ArticleTagId);
                on = false;
            }
            else
            {
                service.AddTag(articleId, tagId, Transaction.Current.UserId.Value);
                on = true;
            }
            return on;
        }

        /// <summary>
        /// deletes the given tag
        /// </summary>
        public static void DeleteTag(int tagId)
        {
            ITagService service = ObjectFactory.GetInstance<ITagService>();
            service.Remove(tagId);
        }

        /// <summary>
        /// retrieves a collection of all available tags
        /// </summary>
        public static TagCollection GetAllTags()
        {
            ITagService service = ObjectFactory.GetInstance<ITagService>();
            return service.FetchAll();
        }

        /// <summary>
        /// returns a list of pages in the system that are of the 'template' type
        /// </summary>
        public static TemplateCollection GetTemplates()
        {
            IPageService pageService = ObjectFactory.GetInstance<IPageService>();
            return pageService.FetchAllTemplates();
        }

        /// <summary>
        /// saves the category passed in
        /// </summary>
        /// <param name="editedCategory"></param>
        public static void SaveCategory(ArticleCategory editedCategory)
        {
            ICategoryService service = ObjectFactory.GetInstance<ICategoryService>();
            service.Save(editedCategory, Transaction.Current.UserId.Value);
        }

        /// <summary>
        /// Retrieve the template that category pages will use
        /// </summary>
        public static ImpactPage GetCategoryTemplate()
        {
            IConfigProvider config = ObjectFactory.GetInstance<IConfigProvider>();
            IPageService pageService = ObjectFactory.GetInstance<IPageService>();
            return pageService.FetchByKey(config.Settings.CategoryTemplate);
        }

        /// <summary>
        /// Retrieve the template that article pages will use
        /// </summary>
        public static ImpactPage GetArticleTemplate()
        {
            IConfigProvider config = ObjectFactory.GetInstance<IConfigProvider>();
            IPageService pageService = ObjectFactory.GetInstance<IPageService>();
            return pageService.FetchByKey(config.Settings.ArticleTemplate);
        }

        /// <summary>
        /// Retrieve the template that tag pages will use
        /// </summary>
        public static ImpactPage GetTagTemplate()
        {
            IConfigProvider config = ObjectFactory.GetInstance<IConfigProvider>();
            IPageService pageService = ObjectFactory.GetInstance<IPageService>();
            return pageService.FetchByKey(config.Settings.TagTemplate);
        }

        /// <summary>
        /// responsible for committing a changed article comment
        /// </summary>
        /// <param name="comment">the object that needs to be stored</param>
        /// <param name="userHostAddress">the IP address of the user host that made the comment</param>
        public static void SaveArticleComment(ArticleComment comment, string userHostAddress)
        {
            IArticleService service = ObjectFactory.GetInstance<IArticleService>();
            service.SaveComment(comment, userHostAddress);
        }

        /// <summary>
        /// get the number of articles in the given category
        /// </summary>
        public static int GetCategoryArticleCount(int categoryId)
        {
            ICategoryService service = ObjectFactory.GetInstance<ICategoryService>();
            return service.GetCategoryArticleCount(categoryId);
        }

        /// <summary>
        /// Retrieve all the comments for an article
        /// </summary>
        public static ArticleCommentCollection GetArticleComments(int articleId)
        {
            IArticleService service = ObjectFactory.GetInstance<IArticleService>();
            return service.FetchComments(articleId);
        }

        #endregion

    }
}
